package com.hanwei.uap.acceptors.sstn.server;

import com.hanwei.uap.acceptors.api.constants.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.Slf4JLoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * 小型站服务端启动定制程序
 *
 * @author zhaoam
 * @create 2017-08-01 15:39
 **/

@Component
public class NettyServer {
    private static final Logger log = LoggerFactory.getLogger(NettyServer.class);
    private volatile EventLoopGroup bossGroup;
    private volatile EventLoopGroup workerGroup;
    private volatile ServerBootstrap bootstrap;
    private volatile boolean closed = false;
    private String serverHost = Server.DEFAULT_BIND_HOST;
    private int serverPort = Server.DEFAULT_BIND_PORT;
    @Autowired
    private Environment env;
    @Autowired
    @Qualifier("nettyServerInitializer")
    private ChannelInitializer channelInitializer;
    @PostConstruct
    private void init() {
        InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE);
        if(env.getProperty("uap.server.host")!=null){
            serverHost = env.getProperty("uap.server.host");
        }
        if(env.getProperty("uap.server.port")!=null){
            serverPort = env.getProperty("uap.server.port",Integer.class);
        }
    }
    public void start() {
        closed = false;
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup);
        bootstrap.channel(NioServerSocketChannel.class);
        bootstrap.handler(new LoggingHandler(LogLevel.INFO));
        bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
        bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
        bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
		bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.childHandler(channelInitializer);
        doBind();
    }

    protected void doBind() {
        if (closed) {
            return;
        }
        bootstrap.bind(new InetSocketAddress(serverHost, serverPort)).addListener((ChannelFutureListener) (ChannelFuture future) -> {
            if (future.isSuccess()) {
                log.info("Server start listen at [{}:{}] success!", serverHost, serverPort);
            } else {
                log.error("Server start listen at [{}:{}] failed!", serverHost, serverPort);
                future.channel().eventLoop().schedule(this::doBind, 1, TimeUnit.SECONDS);
            }
        });
    }

    @PreDestroy
    public void close() {
        closed = true;
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        log.info("Server stop listen at [{host}:{port}] success!", serverHost, serverPort);
    }
}
